package com.spark.gaea.utils.componet;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.Advised;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

import java.util.Map;

@Component
public class ComponentContext implements ApplicationContextAware {
	private static final Logger LOG;
	private static ApplicationContext context;

	@Override
	public void setApplicationContext(final ApplicationContext applicationContext) {
		ComponentContext.context = applicationContext;
	}

	public ApplicationContext getApplicationContext() {
		return ComponentContext.context;
	}

	public <T> T getComponent(final String name) {
		assert ComponentContext.context != null;
		return (T) ComponentContext.context.getBean(name);
	}

	public <T> T getComponent(final Class<T> beanType) {
		assert ComponentContext.context != null;
		final Map<String, T> matchedTypes = this.getComponentsOfType(beanType);
		if (matchedTypes.size() > 0) {
			for (final Map.Entry<String, T> entry : matchedTypes.entrySet()) {
				final Primary primary = this.getTargetClass(entry.getValue()).getAnnotation(Primary.class);
				if (primary != null) {
					return entry.getValue();
				}
			}
			if (matchedTypes.size() > 1) {
				ComponentContext.LOG
						.warn("\u65e0\u6cd5\u5b9a\u4f4d\u552f\u4e00\u7c7b\u578bbean\uff1a" + beanType.getName());
				for (final Map.Entry<String, T> entry : matchedTypes.entrySet()) {
					ComponentContext.LOG.warn("\u5019\u9009\uff1a" + this.getTargetClass(entry.getValue()).getName());
				}
			}
			return (T) matchedTypes.values().toArray()[0];
		}
		throw new NoSuchBeanDefinitionException(beanType.getName());
	}

	public <T> Map<String, T> getComponentsOfType(final Class<T> beanType) {
		return (Map<String, T>) ComponentContext.context.getBeansOfType((Class) beanType);
	}

	public Class<?> getTargetClass(Object instance) {
		while (instance instanceof Advised) {
			try {
				instance = ((Advised) instance).getTargetSource().getTarget();
				continue;
			} catch (Exception e) {
				return instance.getClass();
			}
		}
		return instance.getClass();
	}

	public <T> T getTargetObject(Object instance) {
		while (instance instanceof Advised) {
			try {
				instance = ((Advised) instance).getTargetSource().getTarget();
				continue;
			} catch (Exception e) {
				return (T) instance;
			}
		}
		return (T) instance;
	}

	public <T> T inject(final Class<T> clz) {
		try {
			final T instance = clz.newInstance();
			return this.inject(instance);
		} catch (InstantiationException e) {
			ComponentContext.LOG.error("\u65e0\u6cd5\u6355\u83b7\u7684InstantiationException", e);
			throw new RuntimeException("\u65e0\u6cd5\u6839\u636e\u7c7b[" + clz.getName()
					+ "]\u5b9e\u4f8b\u5316\u5bf9\u8c61\uff0c\u8bf7\u786e\u4fdd\u6709\u516c\u7528\u6784\u9020\u51fd\u6570");
		} catch (IllegalAccessException e2) {
			ComponentContext.LOG.error("\u65e0\u6cd5\u6355\u83b7\u7684IllegalAccessException", e2);
			throw new RuntimeException("\u65e0\u6cd5\u6839\u636e\u7c7b[" + clz.getName()
					+ "]\u5b9e\u4f8b\u5316\u5bf9\u8c61\uff0c\u8bf7\u786e\u4fdd\u6709\u516c\u7528\u6784\u9020\u51fd\u6570");
		}
	}

	public <T> T inject(final Object instance) {
		final AutowireCapableBeanFactory beanFactory = ComponentContext.context.getAutowireCapableBeanFactory();
		beanFactory.autowireBean(instance);
		return (T) instance;
	}

	static {
		LOG = LoggerFactory.getLogger(ComponentContext.class);
	}
}
